package top.lyjwn.todo.common.config;

import com.alibaba.fastjson2.JSON;
import io.lettuce.core.resource.ClientResources;
import io.lettuce.core.resource.DefaultClientResources;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;

//@EnableCaching
@Configuration
public class CachingConfig extends CachingConfigurerSupport implements EnvironmentAware {


    private Environment environment;

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }


    @Bean(destroyMethod = "shutdown")
    @ConditionalOnMissingBean(ClientResources.class)
    public DefaultClientResources lettuceClientResources() {
        return DefaultClientResources.create();
    }

    /**
     * Lettuce 连接工厂.
     *
     * @return 配置好默认的的Lettuce连接工厂
     */
    @Bean
    public LettuceConnectionFactory lettuceConnectionFactory( DefaultClientResources defaultClientResources) {
        String host = environment.getProperty("lyjwn.redis.host","127.0.0.1");
        String port_str = environment.getProperty("lyjwn.redis.port","6379");
        int port = port_str!=null?Integer.parseInt(port_str):null;
        String password = environment.getProperty("lyjwn.redis.password");
        GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
        String maxActive_str = environment.getProperty("lyjwn.redis.maxActive");
        if (maxActive_str!=null)
        {
            genericObjectPoolConfig.setMaxIdle(Integer.parseInt(maxActive_str));
        }

        String maxIdle_str = environment.getProperty("lyjwn.redis.maxIdle");
        if (maxIdle_str!=null)
        {
            genericObjectPoolConfig.setMinIdle(Integer.parseInt(maxIdle_str));
        }
        String minIdle_str = environment.getProperty("lyjwn.redis.minIdle");
        if (minIdle_str!=null)
        {
            genericObjectPoolConfig.setMinIdle(Integer.parseInt(minIdle_str));
        }
        String maxWait_str = environment.getProperty("lyjwn.redis.maxWait");
        if (maxWait_str!=null)
        {
            int maxWait = Integer.parseInt(maxWait_str);
            genericObjectPoolConfig.setMaxWait(Duration.ofMillis(maxWait));
        }
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
        String index_str = environment.getProperty("lyjwn.redis.index");
        int index = index_str!=null?Integer.parseInt(index_str):0;
        redisStandaloneConfiguration.setDatabase(index);
        redisStandaloneConfiguration.setHostName(host);
        redisStandaloneConfiguration.setPort(port);
        redisStandaloneConfiguration.setPassword(RedisPassword.of(password));
        LettuceClientConfiguration clientConfig = LettucePoolingClientConfiguration.builder()
                .poolConfig(genericObjectPoolConfig)
                .clientResources(defaultClientResources)
                .build();

        LettuceConnectionFactory factory = new LettuceConnectionFactory(redisStandaloneConfiguration, clientConfig);
        // 重新初始化工厂
        factory.afterPropertiesSet();
        return factory;
    }


    /**
     * 自定义缓存key生成策略 使用方法 @Cacheable(keyGenerator="keyGenerator")
     *
     * @return
     */
    @Bean
    @Override
    public KeyGenerator keyGenerator() {
        return (target, method, params) -> {
            StringBuilder sb = new StringBuilder();
            sb.append(target.getClass().getName());
            sb.append(method.getName());
            for (Object obj : params) {
                sb.append(JSON.toJSONString(obj).hashCode());
            }
            return sb.toString();
        };
    }

    /**
     * Cacheable 注解的默认bean
     * @param factory
     * @return
     */
    @Bean
    public CacheManager initCacheManager(RedisConnectionFactory factory) {
        // 默认过期时间15分钟
        Duration timeToLive = Duration.ofMinutes(15);
        // 默认设置
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(timeToLive)
                .serializeKeysWith(
                        RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new GenericJackson2JsonRedisSerializer()))
                .disableCachingNullValues();
        //
        RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager.RedisCacheManagerBuilder
                .fromConnectionFactory(factory);
        builder.cacheDefaults(config);
        return builder.build();
    }
}
